package com.w.picture.manager.websocket;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.w.picture.manager.auth.SpaceUserAuthManager;
import com.w.picture.manager.auth.model.SpaceUserPermission;
import com.w.picture.manager.auth.model.SpaceUserPermissionConstant;
import com.w.picture.mode.entity.Picture;
import com.w.picture.mode.entity.Space;
import com.w.picture.mode.entity.User;
import com.w.picture.mode.enums.SpaceTypeEnum;
import com.w.picture.service.PictureService;
import com.w.picture.service.SpaceService;
import com.w.picture.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

/**
 * WebSocket 握手拦截器
 * 在 WebSocket 连接前,需要进行权限校验,如果发现用户没有团队空间内编辑图片的权限,则拒绝握手.可以通过定义一个 WsHandshakeInterceptor 来实现
 * 此外,由于 HTTP 和 WebSocket 的区别, 我们不能再后续收到前端消息时直接从 HttpServletRequest 中获取到用户信息,因此需要通过 WebSocket 拦截器,
 * 为即将建立的 WebSocket 会话增加一些属性,比如:登录的用户信息,编辑的图片id等.
 * <p>
 * 通过 实现 HandshakeInterceptor 接口(喊谢科 因特赛普特),重写 beforeHandshake 方法,在 WebSocket 连接前,对用户进行权限校验,如果校验失败,则拒绝握手.
 *
 * @author wzp
 * @since 2025/2/2
 */
@Slf4j
@Component
public class WsHandshakeInterceptor implements HandshakeInterceptor {

    @Resource
    private UserService userService;
    @Resource
    private PictureService pictureService;
    @Resource
    private SpaceService spaceService;
    @Resource
    private SpaceUserAuthManager spaceUserAuthManager;


    /**
     * 在握手发生之前调用的方法。可以在这里进行权限验证、属性设置等操作。
     *
     * @param request    代表客户端的请求信息。
     * @param response   代表服务端的响应信息。
     * @param wsHandler  处理WebSocket连接的处理器。
     * @param attributes 可以在这个Map中添加自定义属性，这些属性可以在WebSocket会话期间使用。
     * @return 如果返回false，则阻止握手；如果返回true，则继续握手过程。
     * @throws Exception 抛出异常表示处理过程中出现了错误。
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        // 如果是HTTP请求，则获取请求参数，并验证用户是否有该图片的权限。
        if (request instanceof ServletServerHttpRequest) {
            HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
            // 获取请求参数
            String pictureId = servletRequest.getParameter("pictureId");
            if (StrUtil.isBlank(pictureId)) {
                log.error("缺少图片参数,拒绝握手");
                return false;
            }
            // 注意: 必须是同源的请求,才会允许握手.拿到对应的sessionId,,localhost 和 127.0.0.1 不同的源,是拿不到sessionId的
            // 这里目前是通过,修改前端启动为127.0.0.1才拿到的.
            User loginUser = userService.getLoginUser(servletRequest);
            if (ObjUtil.isEmpty(loginUser)) {
                log.error("用户未登录,拒绝握手");
                return false;
            }
            // 校验用户是否有该图片的权限
            Picture picture = pictureService.getById(pictureId);
            if (picture == null) {
                log.error("图片不存在,拒绝握手");
                return false;
            }
            Long spaceId = picture.getSpaceId();
            Space space = null;
            if (spaceId != null){
                space = spaceService.getById(spaceId);
                if (space == null){
                    log.error("空间不存在,拒绝握手");
                    return false;
                }

                if (space.getSpaceType() != SpaceTypeEnum.TEAM.getValue()){
                    log.info("不是团队空间,拒绝握手");
                    return false;
                }
            }

            // 校验用户是否有该图片的权限
            List<String> permissionList = spaceUserAuthManager.getPermissionList(space, loginUser);
            if (!permissionList.contains(SpaceUserPermissionConstant.PICTURE_EDIT)){
                log.error("用户没有编辑图片的权限,拒绝握手");
                return false;
            }

            // 设置属性，用于后续使用
            attributes.put("user",loginUser);
            attributes.put("userId", loginUser.getId());
            attributes.put("pictureId", Long.valueOf(pictureId));
        }
        return true;
    }

    /**
     * 在握手完成后调用的方法。可以在这里进行资源清理、日志记录等操作。
     *
     * @param request   代表客户端的请求信息。
     * @param response  代表服务端的响应信息。
     * @param wsHandler 处理WebSocket连接的处理器。
     * @param exception 如果握手过程中发生了异常，则此参数包含那个异常；如果没有异常，则为null。
     */
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
        // 当前实现为空，意味着不做任何额外处理。
    }
}
